package sidecarUtils

import (
	"bufio"
	"bytes"
	"container/list"
	"crypto/md5"
	"encoding/binary"
	"encoding/hex"
	"io"
	"slices"
	"strings"
	"sync"

	"github.com/google/uuid"
	CMap "github.com/orcaman/concurrent-map"
)

// ComputeBytesAddToHead2 ---------------------------------------------------------------------------------------
// --------------------------------------Tools--------------------------------------------
// ---------------------------------------------------------------------------------------
func ComputeBytesAddToHead2(b []byte) []byte {
	value := len(b)
	b0 := (byte)((value >> 24) & 0xFF)
	b1 := (byte)((value >> 16) & 0xFF)
	b2 := (byte)((value >> 8) & 0xFF)
	b3 := (byte)(value & 0xFF)
	src := make([]byte, 4)
	src[3] = b0
	src[2] = b1
	src[1] = b2
	src[0] = b3

	return slices.Concat(src, b)
}

func ComputeBytesLenToBytes(value int) []byte {
	b0 := (byte)((value >> 24) & 0xFF)
	b1 := (byte)((value >> 16) & 0xFF)
	b2 := (byte)((value >> 8) & 0xFF)
	b3 := (byte)(value & 0xFF)
	src := make([]byte, 4)
	src[3] = b0
	src[2] = b1
	src[1] = b2
	src[0] = b3
	return src
}

func ComputeLen(b []byte) int {
	return int(binary.LittleEndian.Uint32(b))
}

func ReadPropToMap(content []byte) map[string]string {
	confs := make(map[string]string)
	sc := bufio.NewScanner(bytes.NewReader(content))
	for sc.Scan() {
		s := strings.TrimSpace(sc.Text())
		if len(s) > 0 && !strings.HasPrefix(s, "#") {
			index := strings.Index(s, "=")
			if index > 0 && index+1 < len(s) {
				pre := s[0:index]
				val := s[index+1:]
				confs[pre] = val
			}
		}
	}
	return confs
}

type Quene struct {
	mu   sync.Mutex
	list *list.List
}

func NewQuene() *Quene {
	return &Quene{
		mu:   sync.Mutex{},
		list: list.New(),
	}
}

func (q *Quene) Push(a any) {
	defer q.mu.Unlock()
	q.mu.Lock()
	q.list.PushBack(a)
}

func (q *Quene) Pop() any {
	defer q.mu.Unlock()
	q.mu.Lock()
	e := q.list.Front()
	q.list.Remove(e)
	return e
}

func GenenalFormToMap(content []byte) map[string]string {
	m := make(map[string]string)
	if len(content) > 0 {
		s := string(content)
		var ss []string
		if strings.Index(s, "&") > 0 {
			ss = strings.Split(s, "&")
		} else {
			ss = make([]string, 1)
			ss[0] = s
		}
		for i := 0; i < len(ss); i++ {
			o := ss[i]
			index := strings.Index(o, "=")
			if index > 0 {
				key := o[0:index]
				if index < (len(o) - 1) {
					value := o[index+1:]
					m[key] = value
				} else {
					m[key] = ""
				}
			}
		}

	}
	return m
}

func UUID() string {
	return strings.ReplaceAll(uuid.New().String(), "-", "")
}

func WriteDataToConn(w io.Writer, data []byte) error {
	writedIndex := 0
	for writedIndex < len(data) {
		toWriteBytes := data[writedIndex:]
		n, err := w.Write(toWriteBytes)
		if err != nil {
			return err
		}
		writedIndex += n
	}
	return nil
}

func ReadDataFromConn(w io.Reader) ([]byte, error) {
	lenBytes, err := ReadLenDataFromConn(w, 4)
	if err != nil {
		return nil, err
	}
	leng := ComputeLen(lenBytes)
	return ReadLenDataFromConn(w, leng)
}

func ReadLenDataFromConn(w io.Reader, leng int) ([]byte, error) {
	buf := make([]byte, leng)
	readedIndex := 0
	for readedIndex < leng-1 {
		n, err := w.Read(buf[readedIndex:])
		if err != nil {
			return nil, err
		}
		readedIndex += n
	}
	return buf, nil
}

func CreateMd5Hash(b []byte) string {
	hash := md5.New()
	hash.Write(b)
	return hex.EncodeToString(hash.Sum(nil))
}

func CreateMd5HashWithLength(b []byte, length int) string {
	h := CreateMd5Hash(b)
	if len(h)-length < 0 {
		return h
	}
	return h[len(h)-length:]
}

func CompareMd5Hash(hashStr string, b []byte) bool {
	c := CreateMd5Hash(b)
	if len(c)-len(hashStr) < 0 {
		return false
	}
	ha := c[len(c)-len(hashStr):]
	return ha == hashStr
}

type QueneStore struct {
	mu       sync.RWMutex
	mapStore CMap.ConcurrentMap
	quene    *list.List
}

func NewQueneStore() *QueneStore {
	return &QueneStore{
		mu:       sync.RWMutex{},
		mapStore: CMap.New(),
		quene:    list.New(),
	}
}

func (q *QueneStore) Put(k string, a any) {
	defer q.mu.Unlock()
	q.mu.Lock()
	preCount := q.mapStore.Count()
	q.mapStore.Set(k, a)
	postCount := q.mapStore.Count()
	if postCount > preCount {
		q.quene.PushBack(k)
	}

}

func (q *QueneStore) Remove(k string) {
	defer q.mu.Unlock()
	q.mu.Lock()
	_, ok := q.mapStore.Get(k)
	if ok {
		q.mapStore.Remove(k)
		for {
			e1 := q.quene.Front()
			if e1 == nil {
				break
			}
			s := e1.Value.(string)
			if s == k {
				q.quene.Remove(e1)
				break
			} else {
				q.quene.PushBack(s)
			}
		}
	}

}

func (q *QueneStore) Get(k string) (any, bool) {
	defer q.mu.RUnlock()
	q.mu.RLock()
	v, ok := q.mapStore.Get(k)
	return v, ok
}

func (q *QueneStore) GetOne() (any, bool) {
	defer q.mu.Unlock()
	q.mu.Lock()
	if q.quene.Len() == 0 {
		return nil, false
	}
	e1 := q.quene.Front()
	if e1 == nil {
		return nil, false
	}
	s := e1.Value.(string)
	v, ok := q.mapStore.Get(s)
	if ok {
		q.quene.Remove(e1)
		return v, true
	}
	return nil, false
}
